package cn.siriusbot.siriuspro.config.bean;

import cn.siriusbot.siriuspro.admin.service.BotService;
import cn.siriusbot.siriuspro.admin.service.IntentService;
import cn.siriusbot.siriuspro.admin.service.ServerConfigService;
import cn.siriusbot.siriuspro.bot.BotApi;
import cn.siriusbot.siriuspro.bot.BotApiBuilder;
import cn.siriusbot.siriuspro.bot.api.impl.bot.ConfigApiImpl;
import cn.siriusbot.siriuspro.bot.application.SiriusApplication;
import cn.siriusbot.siriuspro.bot.plugin.JavaPlugInClient;
import cn.siriusbot.siriuspro.bot.plugin.PlugInFactory;
import cn.siriusbot.siriuspro.bot.plugin.factory.JavaSiriusAppFactory;
import cn.siriusbot.siriuspro.bot.plugin.factory.SiriusAppFactory;
import cn.siriusbot.siriuspro.uitls.AppContextUtil;
import cn.siriusbot.siriuspro.uitls.ApplicationUtils;
import lombok.SneakyThrows;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.*;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

@Component
@Log4j2
public class JavaPlugInPool {


    @Autowired
    PlugInFactory factory;

    @Autowired
    BotPool botPool;

    @Autowired
    ServerConfigService serverConfigService;

    @Autowired
    StatisticsPool statisticsPool;

    @Autowired
    BotService botService;

    @Autowired
    IntentService intentService;

    @Autowired
    BotApiBuilder botApiBuilder;

    @Autowired
    AppContextUtil appContextUtil;

    Map<String, String> map = new ConcurrentHashMap<>();

    @PostConstruct
    void init() {
        loadApps();
    }

    /**
     * 加载全部应用
     */
    public void loadApps() {
        try {
            File file = new File(ApplicationUtils.appsPath);
            File[] files = file.listFiles();
            if (files == null) {
                return;
            }
            for (File appFile : files) {
                loadAppOne(appFile);
            }
        } catch (Throwable e) {
            log.error("加载JavaPlugInPool出错");
        }
    }

    private void loadAppOne(File appFile) {
        try {
            List<Class<?>> appInstance = getAppInstance(appFile);
            SiriusAppFactory siriusAppFactory = new JavaSiriusAppFactory();
            appInstance.forEach(siriusAppFactory::scanComponent);   // 扫描注解

            SiriusApplication component = siriusAppFactory.getComponent(SiriusApplication.class);
            final BotApi botApi = botApiBuilder.build(component.appInfo());

            siriusAppFactory.addBean("botApi", botApi);
            siriusAppFactory.addBean("resourceApi", botApi.resourceApi());
            siriusAppFactory.addBean("siriusUtils", botApi.siriusUtils());
            siriusAppFactory.addBean("configApi", botApi.configApi());
            siriusAppFactory.addBean("botManageApi", botApi.botManageApi());
            siriusAppFactory.addBean("announcesApi", botApi.announcesApi());
            siriusAppFactory.addBean("apiPermissionApi", botApi.apiPermissionApi());
            siriusAppFactory.addBean("audioApi", botApi.audioApi());
            siriusAppFactory.addBean("channelApi", botApi.channelApi());
            siriusAppFactory.addBean("channelPermissionsApi", botApi.channelPermissionsApi());
            siriusAppFactory.addBean("dmsApi", botApi.dmsApi());
            siriusAppFactory.addBean("forumApi", botApi.forumApi());
            siriusAppFactory.addBean("guildApi", botApi.guildApi());
            siriusAppFactory.addBean("messageApi", botApi.messageApi());
            siriusAppFactory.addBean("messageReactionApi", botApi.messageReactionApi());
            siriusAppFactory.addBean("messageSettingApi", botApi.messageSettingApi());
            siriusAppFactory.addBean("noSpeakApi", botApi.noSpeakApi());
            siriusAppFactory.addBean("pinsMessageApi", botApi.pinsMessageApi());
            siriusAppFactory.addBean("roleApi", botApi.roleApi());
            siriusAppFactory.addBean("scheduleApi", botApi.scheduleApi());
            siriusAppFactory.addBean("userApi", botApi.userApi());

            // 初始化组件
            siriusAppFactory.startComponent();
            component.SiriusAppInit(botApi);

            factory.add(
                    new JavaPlugInClient(component, component.appInfo(), siriusAppFactory)
            );
            log.info(String.format("已加载应用 -> [%s] %s", component.appInfo().getPackageName(), component.appInfo().getAppName()));
            map.put(appFile.getAbsolutePath(), component.appInfo().getPackageName());
        } catch (Throwable e) {
            log.error("应用加载失败 -> " + appFile.getName() + "中未有继承SiriusApplication的类，可能不是天狼星应用", e);
        }
    }

    @Autowired
    ConfigApiImpl configApi;

    /**
     * 获取应用实例
     *
     * @param app
     * @return
     */
    @SneakyThrows
    public List<Class<?>> getAppInstance(File app) {
        List<Class<?>> result = new ArrayList<>();
        //获取当前线程类加载器
        ClassLoader parent = Thread.currentThread().getContextClassLoader();  // 启动类加载器

        //获取类加载器
        URLClassLoader appClass = new URLClassLoader(new URL[]{new URL("file:" + app.getAbsolutePath())}, parent);

        JarFile jarFile = new JarFile(app.getAbsolutePath());
        Enumeration<JarEntry> entries = jarFile.entries();
        //遍历jar包里所有文件
        while (entries.hasMoreElements()) {
            JarEntry jarEntry = entries.nextElement();
            //获取资源名称
            String fileName = jarEntry.getName();
            if (fileName.contains(".class")) {
                fileName = fileName.replace("/", ".").replace(".class", "");
                try {
                    Class<?> aClass = appClass.loadClass(fileName);
                    result.add(aClass);
                } catch (Throwable ignored) {

                }
            }
        }
        //log.error("插件加载失败 -> " + jarFile.getName() + "中未有继承SiriusApplication的类，可能不是天狼星应用");
        return result;
    }


    @Async
    public void watchForChanges(Path dir) throws IOException, InterruptedException {
        // 创建WatchService来监视给定目录中的事件
        WatchService watchService = FileSystems.getDefault().newWatchService();
        dir.register(watchService, StandardWatchEventKinds.ENTRY_MODIFY, StandardWatchEventKinds.ENTRY_DELETE);
        while (true) {
            WatchKey key = watchService.take(); // 获取待处理的事件
            for (WatchEvent<?> event : key.pollEvents()) {
                WatchEvent.Kind<?> kind = event.kind();
                if (kind == StandardWatchEventKinds.OVERFLOW) {
                    continue;
                }

                // 监测到了jar文件被修改
                Path filename = Path.of(dir + "/" + event.context());
                if (filename.toString().endsWith(".jar")) {
                    if (kind == StandardWatchEventKinds.ENTRY_MODIFY) {
                        onJarFileModified(filename);
                    } else if (kind == StandardWatchEventKinds.ENTRY_DELETE) {
                        onJarFileDeleted(filename);
                    }
                }
            }

            // 重置WatchKey以接收后续事件
            boolean valid = key.reset();
            if (!valid) {
                break;
            }
        }
    }

    /**
     * 文件变动时
     */
    private void onJarFileModified(Path filename) {
        log.info(String.format("[%s]文件发生了变动，进行热重载", filename));
        loadAppOne(filename.toFile());
    }

    /**
     * 文件删除时
     */
    private void onJarFileDeleted(Path filename) {
        final String absolutePath = filename.toFile().getAbsolutePath();
        if (map.containsKey(absolutePath)) {
            final String packageName = map.get(absolutePath);
            factory.remove(packageName);
        }
    }

}
